/**************************************************************************************
*  j_lang.c
*
*  This file contains native methods for system paclakges defined in the ClassLinker's
*  control file.
*
**************************************************************************************
*
*  The contents of this file are subject to the RTJ COMPUTING non-commercial
*  source code license agreement ("License"); you may not use this file except in
*  compliance with the License. A copy of the License (license.terms) can be
*  found in the root of installation folder or on the web at
*  http://www.rtjcom.com/Files/license.terms
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
*  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*  IN NO EVENT SHALL RTJ COMPUTING BE LIABLE FOR ANY CLAIM, DAMAGES
*  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
*  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
*
*  Copyright (c) 2000-2002 RTJ Computing Pty. Ltd. All rights reserved.
*
**************************************************************************************/

#include "jvm.h"
#include "jcommon.h"

/****************************************************************************
* Copies array items from src starting at src_position to dst at
* dst_position. Number of bytes to copy specifies length parameter.
* Method will generate several exceptions if parameters are not correct.
*
* Java prototype:
*           void arraycopy(Object src, int src_position, Object dst,
*                          int dst_position, int length);
*
****************************************************************************/
static int16 System_arraycopy(int32 par[], int32 *rv)
{
    uint16 count = 0;
    ref_t *ref;
    arrhdr_t *src, *dst;
    uint8 *srcData, *dstData;

    /* verify parameters */
    if (par[4] == 0)
        return NO_EXCEP;

    if (par[1] < 0 || par[3] < 0 || par[4] < 0)
        return EXCEP_IndexOutOfBounds;


    ref = GET_REF(par[0]);
    if (ref == NULL)
        return EXCEP_NullPointer;

    src = (arrhdr_t *)ref->ptr;

    ref = GET_REF(par[2]);
    if (ref == NULL)
        return EXCEP_NullPointer;

    dst = (arrhdr_t *)ref->ptr;

    if (!(src->obj.flag & OF_ARRAY) || !(dst->obj.flag & OF_ARRAY) ||
        (uint16)(par[1]+par[4]) > src->count || (uint16)(par[3]+par[4]) > dst->count ||
        src->type != dst->type)
    {
        return EXCEP_ArrayStore;
    }
    /* prepare pointers for copying */
    srcData = (uint8 *)src + sizeof(arrhdr_t);
    dstData = (uint8 *)dst + sizeof(arrhdr_t);
    switch (src->type)
    {
        case T_BOOLEAN:
        case T_BYTE:
#if !ENABLE_UNICODE
        case T_CHAR:
#endif
            srcData += (uint16)par[1];
            dstData += (uint16)par[3];
            count = (uint16)par[4];
            break;

#if ENABLE_UNICODE
        case T_CHAR:
#endif
        case T_SHORT:
            srcData += (uint16)(par[1] << 1);
            dstData += (uint16)(par[3] << 1);
            count = (uint16)(par[4] << 1);
            break;

        case T_INT:
        case T_FLOAT:
        case T_OBJREF:
            srcData += (uint16)(par[1] << 2);
            dstData += (uint16)(par[3] << 2);
            count = (uint16)(par[4] << 2);
            break;
    }
    /* copy from source to destination, starting from the end of array */
    while (count-- > 0)
        dstData[count] = srcData[count];

    return NO_EXCEP;
}

/****************************************************************************
* Returns amount of free memory on the heap for application objects.
*
* Java prototype:
*           int freeMemory();
*
****************************************************************************/
static int16 System_freeMemory(int32 par[], int32 *rv)
{
    *rv = (frame_ptr - heap_ptr);
    return NO_EXCEP;
}


/****************************************************************************
* Returns total amount of memory on the heap for application objects..
*
* Java prototype:
*           int16 totalMemory();
*
****************************************************************************/
static int16 System_totalMemory(int32 par[], int32 *rv)
{
    *rv = frame_ptr - objdata_start;
    return NO_EXCEP;
}


/****************************************************************************
* Invokes garbage collector. Method returns once the GC finishes its work.
*
* Java prototype:
*           void gc();
*
****************************************************************************/
static int16 System_gc(int32 par[], int32 *rv)
{
#if ENABLE_GC
    walkAll();
    compactHeapForced();
#endif
    return vm_excep;
}


/****************************************************************************
* Returns time in ms since VM started running.
*
* Java prototype:
*           int currentTimeMillis();
*
****************************************************************************/
static int16 System_currentTimeMillis(int32 par[], int32 *rv)
{
    *rv = vm_time_ms;
    return NO_EXCEP;
}

/****************************************************************************
* Not implemented in LINEAR_64K and BANKED_64K memory models. Its here for
* compability with ClassLinker.control file.
*
* Java prototype:
*           static int getPropertyLen(String key);
*
****************************************************************************/
static int16 System_getPropertyLen(int32 par[], int32 *rv)
{
    *rv = 0;
    return NO_EXCEP;
}

/****************************************************************************
* Not implemented in LINEAR_64K and BANKED_64K memory models. Its here for
* compability with ClassLinker.control file.
*
* Java prototype:
*           static boolean getProperty0(String key, byte[] dest);
*
****************************************************************************/
static int16 System_getProperty0(int32 par[], int32 *rv)
{
    *rv = false;
    return NO_EXCEP;
}

/****************************************************************************
* Starts execution of the new thread. There must be enough room in thr_table
* and run() method must be defined.
*
* Java prototype:
*           void start();
****************************************************************************/
static int16 java_lang_Thread_start(int32 par[], int32 *rv)
{
    createThread(GET_REF(par[0]));
    return vm_excep;
}


/****************************************************************************
* Stops the execution of the thread. Information about this thread will be
* removed from thr_table.
*
* Java prototype:
*           void stop();
*
****************************************************************************/
static int16 java_lang_Thread_stop(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    deleteThread(findThread(GET_REF(par[0])));
#endif
    return vm_excep;
}


/****************************************************************************
* Suspends the execution of the thread by setting the corresponding flag in
* the thr_table.
*
* Java prototype:
*           void suspend();
*
****************************************************************************/
static int16 java_lang_Thread_suspend(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    thread_t *thread;

    thread = findThread(GET_REF(par[0]));
    if (thread != NULL)
    {
        thread->flag |= FLG_SUSPEND;

        /* if current thread is suspended -- force thread switch */
        if (thread == thr_active)
            thr_switch++;
    }
#endif
    return vm_excep;
}


/****************************************************************************
* Resumes the execution of the thread by clearing the corresponding flag in
* the thr_table.
*
* Java prototype:
*           void resume();
*
****************************************************************************/
static int16 java_lang_Thread_resume(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    thread_t *thread;

    thread = findThread(GET_REF(par[0]));
    if (thread != NULL)
    {
        thread->flag &= ~FLG_SUSPEND;

        /* force thread switch */
        thr_switch++;
    }
#endif
    return vm_excep;
}


/****************************************************************************
* Allows other threads to run by forcing the thread switch.
*
* Java prototype:
*           void yield();
*
****************************************************************************/
static int16 java_lang_Thread_yield(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    thr_switch++;
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Checks if the thread is running (present in the thr_table). Returns
* true if thread is running otherwise returns false.
*
* Java prototype:
*           boolean isAlive();
*
****************************************************************************/
static int16 java_lang_Thread_isAlive(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    *rv = true;
    if (findThread(GET_REF(par[0])) == NULL)
        *rv = false;
#else
    *rv = true;
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Sets the sleep counter for current thread to milisec value (32bit).
* Current thread will be suspended until counter decrements to zero.
*
* Java prototype:
*           void sleep(int milisec);
*
****************************************************************************/
static int16 java_lang_Thread_sleep0(int32 par[], int32 *rv)
{
    thr_active->sleep = par[0];
    thr_active->flag |= FLG_SLEEP;
    thr_switch++;
    return NO_EXCEP;
}


/****************************************************************************
* Returns object reference of currently running thread.
*
* Java prototype:
*       Thread currentThread();
*
****************************************************************************/
static int16 java_lang_Thread_currentThread(int32 par[], int32 *rv)
{
    *rv = PUT_REF(thr_active->objref);
    return NO_EXCEP;
}


/****************************************************************************
* Goes trough the current thread frame stack and fills in stack_trace
* buffer for the current thread.
*
* Java prototype:
*           void fillInStackTrace();
*
****************************************************************************/
static int16 java_lang_Throwable_fillInStackTrace(int32 par[], int32 *rv)
{
    fillInStackTrace();
    return NO_EXCEP;
}


/****************************************************************************
* Copies stack_trace buffer into provided destination buffer. Number of
* copied bytes is limited by the size of byte array Buffer or the size of
* stack_trace buffer.
*
* Java prototype:
*           void getStackTrace(byte[] Buffer);
*
****************************************************************************/
static int16 java_lang_Throwable_getStackTrace(int32 par[], int32 *rv)
{
    ref_t    *ref;
    arrhdr_t *dst;
    uint16   i;
    trace_tbl_t *dst_buf;

    /* verify destination buffer */
    ref = GET_REF(par[1]);
    if (ref == NULL)
    {
        return EXCEP_NullPointer;
    }
    dst = (arrhdr_t *)ref->ptr;
    if (dst->count < (uint16)(config->stack_trace->count * sizeof(trace_tbl_t)))
    {
        return EXCEP_IndexOutOfBounds;
    }

    dst_buf = (trace_tbl_t *)(dst + sizeof(arrhdr_t));
    for (i=0; i < (uint16)config->stack_trace->count; i++)
    {
        dst_buf[i] = config->stack_trace->trace[i];
    }

    return NO_EXCEP;
}


/****************************************************************************
* Returns object reference as hashcode value for this object.
*
* Java prototype:
*           int hashCode();
*
****************************************************************************/
static int16 java_lang_Object_hashCode(int32 par[], int32 *rv)
{
    ref_t *ref = GET_REF(par[0]);
    *rv = ((uint32)ref << 16) | (uint16)ref->class_ptr;
    return NO_EXCEP;
}


/****************************************************************************
* Interrupts thread from sleep or wait state.
*
* Java prototype:
*           void interrupt0();
****************************************************************************/
static int16 java_lang_Thread_interrupt0(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    thread_t *thread;
    ref_t *ref;

    thread = findThread(GET_REF(par[0]));
    if (thread != NULL)
    {
        if (thread->flag & FLG_SLEEP)
        {
            thread->sleep = 0;
            thread->flag &= ~FLG_SLEEP;
        }
        if (thread->flag & FLG_WAIT_NOTIFY)
        {
            thread->flag &= ~FLG_WAIT_NOTIFY;
            thread->wait = 0;
            ref = thread->wait_obj;
            thread->wait_obj = NULL;
            acquireObjectLock(thread, ref);
        }
    }
#endif
    return NO_EXCEP;
}

/****************************************************************************
* Sets the daemon flag for the thread referenced by objref (par[0]).
*
* Java prototype:
*           void setDaemon0(boolean set);
****************************************************************************/
static int16 java_lang_Thread_setDaemon0(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    ref_t *ref;

    ref = GET_REF(par[0]);
    if (ref == NULL)
      return EXCEP_NullPointer;

    if (par[1])
        ref->ptr->flag |= OF_DAEMON;
    else
        ref->ptr->flag &= ~OF_DAEMON;
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Releases all locks for this object and current thread enters wait state for
* specified number of milliseconds. If millis is 0 then this thread waits
* forever or until interrupted.
*
* Java prototype:
*           void wait0(int millis);
*
****************************************************************************/
static int16 java_lang_Object_wait0(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    ref_t *ref;

    /* Current thread must have a lock on a object on which the wait is invoked */
    ref = GET_REF(par[0]);
    if (ref->ptr->thread_id != thr_active->id)
        return EXCEP_IllegalMonitState;

    thr_active->flag |= FLG_WAIT_NOTIFY;
    thr_active->wait = par[1];
    thr_active->wait_locks = ref->ptr->locks;
    thr_active->wait_obj = ref;

    /* release all locks associated with this object */
    ref->ptr->locks = 1;
    if (!releaseObjectLock(ref))
        return EXCEP_IllegalMonitState;

    thr_switch++;
#endif
    return NO_EXCEP;
}

/****************************************************************************
* Helper function for notify and notifyAll native methods.
* Checks the threads table if there is a thread (or more) that are waiting
* to be notified;
*
* Input:        par         - parameter containg embedded object's reference
*               notifyOne   - set to true if only one thread should be notified
* Output:       none
* Return:       NO_EXCEP if all went Ok, EXCEP_IllegalMonitState if current
*               thread didn't own object's monitor.
****************************************************************************/
#if ENABLE_THREADS
static int16 notifyWaitingThread(int32 par, bool notifyOne)
{
    uint16 i;
    ref_t *ref;

    /* Current thread must have a lock on a object on which the notify is invoked */
    ref = GET_REF(par);
    if (ref->ptr->thread_id != thr_active->id)
        return EXCEP_IllegalMonitState;

    for (i=0; i < config->threads; i++)
    {
        if ((threads[i].flag & (FLG_USED | FLG_WAIT_NOTIFY)) == (FLG_USED | FLG_WAIT_NOTIFY) &&
            threads[i].wait_obj == ref)
        {
            threads[i].flag &= ~FLG_WAIT_NOTIFY;
            threads[i].wait = 0;
            threads[i].wait_obj = NULL;
            acquireObjectLock(&threads[i], ref);
            if (notifyOne)
                break;
        }
    }
    return NO_EXCEP;
}
#endif

/****************************************************************************
* Notifies first thread waiting on this object. Thread is removed from wait
* wait state and continues execution.
*
* Java prototype:
*           void notify();
*
****************************************************************************/
static int16 java_lang_Object_notify(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    return notifyWaitingThread(par[0], true);
#else
    return NO_EXCEP;
#endif
}


/****************************************************************************
* Notifies all threads waiting on this object. Threads are removed from wait
* wait state and continue execution.
*
* Java prototype:
*           void notifyAll();
*
****************************************************************************/
static int16 java_lang_Object_notifyAll(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    return notifyWaitingThread(par[0], false);
#else
    return NO_EXCEP;
#endif
}


/****************************************************************************
* Not implemented in LINEAR_64K and BANKED_64K memory models. Its here for
* compability with ClassLinker.control file.
*
* Java prototype:
*           Object clone0(Object obj);
*
****************************************************************************/
static int16 java_lang_Object_clone0(int32 par[], int32 *rv)
{
    *rv = 0;
    return NO_EXCEP;
}


/****************************************************************************
* Converts float number into integer value.
*
* Java prototype:
*           static int floatToIntBits(float value);
*
****************************************************************************/
static int16 java_lang_Float_floatToIntBits(int32 par[], int32 *rv)
{
#if ENABLE_FLOAT
    *rv = par[0];
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Converts integer number into float value.
*
* Java prototype:
*           static float intBitsToFloat(int bits);
*
****************************************************************************/
static int16 java_lang_Float_intBitsToFloat(int32 par[], int32 *rv)
{
#if ENABLE_FLOAT
    *rv = par[0];
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Parses input stream of ASCII characters terminated with zero and returns
* float value.
*
* Java prototype:
*           static float parseFloat(byte[] s);
*
****************************************************************************/
static int16 java_lang_Float_parseFloat(int32 par[], int32 *rv)
{
#if ENABLE_FLOAT
    float32 val;
    arrhdr_t *arr;

    arr = (arrhdr_t *)(GET_REF(par[0]))->ptr;
    if (stringToFloat((uint8 *)arr + sizeof(arrhdr_t), &val))
      *rv = *((uint32 *)&val);
    else
      *rv = NaN;
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Converts specified float value into ASCII characters.
*
* Java prototype:
*           static int toString0(float f, byte dest[]);
*
****************************************************************************/
static int16 java_lang_Float_toString0(int32 par[], int32 *rv)
{
#if ENABLE_FLOAT
    float32 fval;
    arrhdr_t *arr;

    *((int32 *)&fval) = par[0];
    arr = (arrhdr_t *)(GET_REF(par[1]))->ptr;
    if (arr->count > 0)
        *rv = floatToString(fval, (uint8 *)arr + sizeof(arrhdr_t), arr->count);
    else
        *rv = 0;
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Clears all event counters.
*
* Input:    none
* Output:   none
* returns:  none
****************************************************************************/
#if ENABLE_THREADS
static void clearEventCounters(void)
{
    uint16 i;

    for (i=0; i < config->events; i++)
        config->ev_counters[i] = 0;
}
#endif

/****************************************************************************
* Returns the number of event fire counters as specified in the configuration
* structure.
*
* Java prototype:       int getTimersCount();
****************************************************************************/
static int16 javax_events_Events_getEventsCount(int32 par[], int32 *rv)
{
    *rv = config->events;

    return NO_EXCEP;
}


/****************************************************************************
* Registers current thread with the VM's thread execution scheduler. When an event
* is recognised thread scheduler will instantly resume execution of this thread
* so that appropriate fire() methods of AsyncEvent classes can be invoked.
*
* Java prototype:       void eventThreadRegister();
****************************************************************************/
static int16 javax_events_Events_eventThreadRegister(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    if (config->events > 0 && config->ev_counters != NULL)
    {
        ev_thread = findThread(GET_REF(par[0]));
        if (ev_thread == NULL)
            return EXCEP_NullPointer;

        ev_index = 0;
        clearEventCounters();
    }
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Get an ID of the next pending event.
*
* Java prototype:       int getEvent();
****************************************************************************/
static int16 javax_events_Events_getEvent(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    uint16 i;

    *rv = -1;
    if (ev_thread != NULL)
    {
        for (i=0; i < config->events; i++)
        {
            if (config->ev_counters[ev_index] > 0)
            {
                /*
                ** Found pending event fire counter -- get out of the loop and
                ** allow to set ev_index to the next fire counter.
                */
                i = config->events;
                *rv = ev_index;
                config->ev_counters[ev_index]--;
            }
            /* move to the next event fire counter */
            if (++ev_index == config->events)
                ev_index = 0;
        }
    }
#endif
    return NO_EXCEP;
}


/****************************************************************************
* This method will clear fire counter for the event specified by its ID.
* If ALL_EVENTS (-1) is specified then all fire counters are cleared.
*
* Java prototype:       void clearPendingFireCount(int eventId);
****************************************************************************/
static int16 javax_events_Events_clearPendingFireCount(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    if (ev_thread != NULL)
    {
        if (par[1] >= 0)
            config->ev_counters[par[1]] = 0;
        else
            clearEventCounters();
    }
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Returns id of the handler for which timeout value has expired. -1 is returned
* if no timer has expired.
*
* Java prototype:       int getExpiredId();
****************************************************************************/
static int16 javax_events_Timer_getExpiredId(int32 par[], int32 *rv)
{
#if ENABLE_THREADS
    uint16 i;

    *rv = -1;
    if (sw_timers != NULL)
    {
        for (i=0; i < config->timers; i++)
        {
            if (sw_timers[i].flag == TMR_EXPIRED)
            {
                /* expired timer found -- clear the expired flag and return it's index */
                sw_timers[i].flag = 0;
                *rv = i;
                break;
            }
        }
    }
#endif
    return NO_EXCEP;
}


/****************************************************************************
* Starts the timer with specified ID and timeout value.
*
* Java prototype:       start0(int timerId, int timeout);
****************************************************************************/
static int16 javax_events_Timer_start0(int32 par[], int32 *rv)
{
    if (sw_timers != NULL)
    {
        sw_timers[par[1]].time = par[2];
        sw_timers[par[1]].flag = 0;
    }

    return NO_EXCEP;
}


/****************************************************************************
* Returns the number of software timers as specified in the configuration
* structure.
*
* Java prototype:       int getTimersCount();
****************************************************************************/
static int16 javax_events_Timer_getTimersCount(int32 par[], int32 *rv)
{
    *rv = config->timers;

    return NO_EXCEP;
}


/*
** System native method lookup table.
** IMPORTANT:
**   Order of methods this table must exactly match the method order in
**   the ClassLinker.control file. Otherwise, incorrect native method might
**   be invoked and can eventually cause the system to crash!
*/
const native_func_ptr sys_native_tbl[] =
{
    /* java.lang.System methods */
    System_arraycopy,
    System_freeMemory,
    System_totalMemory,
    System_gc,
    System_currentTimeMillis,
    System_getPropertyLen,
    System_getProperty0,
    /* java.lang.Thread methods */
    java_lang_Thread_start,
    java_lang_Thread_stop,
    java_lang_Thread_suspend,
    java_lang_Thread_resume,
    java_lang_Thread_yield,
    java_lang_Thread_isAlive,
    java_lang_Thread_sleep0,
    java_lang_Thread_interrupt0,
    java_lang_Thread_currentThread,
    java_lang_Thread_setDaemon0,
    /* java.lang.Throwable methods */
    java_lang_Throwable_fillInStackTrace,
    java_lang_Throwable_getStackTrace,
    /* java.lang.Object methods */
    java_lang_Object_hashCode,
    java_lang_Object_wait0,
    java_lang_Object_notify,
    java_lang_Object_notifyAll,
    java_lang_Object_clone0,
    /* java.lang.Float methods */
    java_lang_Float_floatToIntBits,
    java_lang_Float_intBitsToFloat,
    java_lang_Float_parseFloat,
    java_lang_Float_toString0,
    /* javax.events package */
    javax_events_Events_getEventsCount,
    javax_events_Events_eventThreadRegister,
    javax_events_Events_getEvent,
    javax_events_Events_clearPendingFireCount,
    javax_events_Timer_getExpiredId,
    javax_events_Timer_start0,
    javax_events_Timer_getTimersCount,
};




